import java.util.LinkedList;

public class GenericSearch
{

	Problem			myProblem;
	SearchStrategy	usedStrategy;
	int  allNodes;
	static int c;

	public GenericSearch(Problem myProblem, SearchStrategy myStrategy)
	{
		super();
		this.myProblem = myProblem;
		this.usedStrategy = myStrategy;
	}

	public Problem getMyProblem()
	{
		return myProblem;
	}

	public void setMyProblem(Problem myProblem)
	{
		this.myProblem = myProblem;
	}

	public SearchStrategy getMyStrategy()
	{
		return usedStrategy;
	}

	public void setMyStrategy(SearchStrategy myStrategy)
	{
		this.usedStrategy = myStrategy;
	}

	public Node gnSearch()
	{
		//System.out.println("Inside method gnSearch");
		// list of nodes that are to be explored
		LinkedList<Node> visitedNodes = new LinkedList<Node>();
		
		// defining the parameters of the root node
		Node parent = null;
		LinkedList<Node> children = new LinkedList<Node>();
		Board rootBoard = getMyProblem().getInitialState().getCurrentBoard().copyBoard(getMyProblem().getInitialState().getCurrentBoard());
		State currentState = new State(rootBoard);
		String currentMovement = ""; // operator used
		LinkedList<String> movementsSoFar = new LinkedList<String>();
		int currentLevel = 0;
		Block blocktoBeMoved = null;

		// initialize the root
		Node root = new Node(parent, children, movementsSoFar, currentLevel,
				currentMovement, currentState, blocktoBeMoved);

		// adding the root in the to be explored List
		visitedNodes.add(root);
		allNodes++;
		//set score here
		
		Node exploredNode;
		
		int i = 0;
		if(getMyStrategy().getTypeOfSearch().equals("ID")&& c<300)
		{ c++;
			exploredNode = visitedNodes.pop();
			
			if ((exploredNode.getLevel()== 0||exploredNode.getBlocktoBeMoved() != null)
					&& exploredNode.isGoal(exploredNode) )
			{
				
				return exploredNode;
				
			}
			
			LinkedList<Node> newNodes = new LinkedList<Node>();
			newNodes = getMyStrategy().search(visitedNodes, exploredNode);
			if(!newNodes.isEmpty()&& newNodes.size()==1)
			return newNodes.pop();
				
			getMyStrategy().setDFCounter(getMyStrategy().getDFCounter()+1);
			getMyStrategy().setI(0);
			return gnSearch();
		}
		else 
		{
				do
				{
					// no more nodes to visit
					if (visitedNodes.isEmpty())
					{
						return null;
					}
		
					exploredNode = visitedNodes.pop();
					// return the state at which the goal is reached
					if ((exploredNode.getLevel()== 0||exploredNode.getBlocktoBeMoved() != null)
							&& exploredNode.isGoal(exploredNode) )
					{
						return exploredNode;
					}
					//System.out.println("enter here");
					// Use the search strategy here
					LinkedList<Node> newNodes = new LinkedList<Node>();
					newNodes = getMyStrategy().search(visitedNodes, exploredNode);
		
					// New nodes have already been visited, add them to visitedNodes
					visitedNodes = addListToList(visitedNodes, newNodes);
					allNodes =allNodes+newNodes.size();
					i++;
					//System.out.println("visited "+ visitedNodes.size());
				} while (!visitedNodes.isEmpty()
						&& i<6);
				//System.out.println();
				
				while(!visitedNodes.isEmpty()&& getMyStrategy().getTypeOfSearch().equals("DF"))
				{
					exploredNode = visitedNodes.pop();
					// return the state at which the goal is reached
					if ((exploredNode.getLevel()== 0||exploredNode.getBlocktoBeMoved() != null)
							&& exploredNode.isGoal(exploredNode) )
					{
						return exploredNode;
					}
				}
				
		}		
		return null;
	}

	/**
	 * Adds a list to another list 
	 * @param visitedNodes
	 * @param newNodes
	 * @return
	 */
	public LinkedList<Node> addListToList(LinkedList<Node> visitedNodes,
			LinkedList<Node> newNodes)
	{
		int size = newNodes.size();
		for (int i = 0; i < size; i++)
		{
			visitedNodes.addLast(newNodes.pop());
		}
		return visitedNodes;

	}




}
